<?php

namespace App\Http\Controllers\Gateway;

use App\Exceptions\RechargeGatewayException;
use App\Facades\RechargeLog;
use App\Jobs\SendRechargeCallback;
use App\Lib\Code;
use App\Lib\GatewayCode;
use App\Lib\PaymentMap;
use App\Lib\XDeode;
use App\Models\PlatUser;
use App\Models\RechargeGroupPayment;
use App\Models\RechargeIf;
use App\Models\RechargeOrder;
use App\Repositories\Eloquent\PlatUserAppRepository;
use App\Services\ApiResponseService;
use App\Services\Gateway\RechargeGatewayService;
use App\Services\GatewayResponseService;
use App\Services\RechargeOrderNotifyService;
use App\Services\RechargeOrderService;
use App\Services\RechargeSplitModeService;
use App\Services\ThirdPayments\Contracts\QRCapable;
use App\Services\ThirdPayments\Contracts\RechargePaymentFactory;
use App\Services\ThirdPayments\Contracts\SDKable;
use App\Services\ThirdPayments\Contracts\WAPable;
use App\Validators\Gateway\RechargeGatewayValidator;
use Carbon\Carbon;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\HtmlString;
use Jenssegers\Agent\Agent;
use Prettus\Validator\Exceptions\ValidatorException;

class RechargeGatewayController extends Controller
{
    use DispatchesJobs;
    //
    protected $validator;
    protected $gatewayService;
    protected $orderService;
    protected $rechargeFactory;

    public function __construct(RechargeGatewayValidator $rechargeGatewayValidator,
                                RechargeGatewayService $rechargeGatewayService,
                                RechargeOrderService $rechargeOrderService,
                                RechargePaymentFactory $rechargePaymentFactory)
    {
        $this->validator = $rechargeGatewayValidator;
        $this->gatewayService = $rechargeGatewayService;
        $this->orderService = $rechargeOrderService;
        $this->rechargeFactory = $rechargePaymentFactory;
    }

    public function pay(Request $request,
                        RechargeSplitModeService $rechargeSplitModeService)
    {

        try {
            $data = $request->all(array_keys($this->validator->getRules('pay')));
            $data = array_filter($data);
            $this->validator->with($data)->passesOrFail('pay');
            if ( ! $this->gatewayService->verifySign($data)) {
                return GatewayResponseService::fieldError(['sign' => GatewayCode::SIGN_NOT_MATCH]);
            }
            $platuser = PlatUser::bycode($data['mch_code'])
                ->with(['profile' => function ($query) {
                    $query->select('uid', 'status', 'role');
                }])->select('id', 'status', 'upper_id', 'key', 'code', 'settle_cycle', 'recharge_api', 'recharge_mode', 'recharge_gid')
                ->first();
            if ( ! $app = $platuser->apps()->where('app_id', $data['app_id'])->first()) {
                return GatewayResponseService::fieldError(['app_id' => GatewayCode::USER_APP_NOT_MATCH]);
            }
            if ($app->status != 1) {
                return GatewayResponseService::fieldError(['app_id' => GatewayCode::APP_NOT_PERMITTED]);
            }
            if ( ! config('app.website.GATEWAY_SANDBOX')) {
                $domain = $app->domain;
                $domain = explode(':', $domain);
                if (filter_var($domain[0], FILTER_VALIDATE_IP)) {
                    if ($domain[0] != $request->getClientIp()) {
                        return GatewayResponseService::codeError(GatewayCode::REFERER_DOMAIN_NOT_MATCH);
                    }
                } else {
                    if ($app->domain != '*') {
                        $pares = parse_url($request->header('referer'));
                        try {
                            $parse_domain = $pares['host'];
                            if (array_key_exists('port', $pares)) {
                                $parse_domain = $pares['host'] . ':' . $pares['port'];
                            }
                        } catch (\Exception $exception) {
                            return GatewayResponseService::codeError(GatewayCode::REFERER_PARSE_WRONG);
                        }
                        if ($app->domain != $parse_domain) {
                            return GatewayResponseService::codeError(GatewayCode::REFERER_DOMAIN_NOT_MATCH);
                        }
                    }
                }
            }
            $group_payment = $this->gatewayService->getPayment($platuser, $data['recharge_type']);
            if ( ! $group_payment instanceof RechargeGroupPayment) {
                return $group_payment;
            }
            $recharge_if = $rechargeSplitModeService->getUsableInterfaceBySplitMode($group_payment->splitmode);
            if (! $recharge_if) {
                throw new \Exception('第三方接口厂商配置无效');
//                return GatewayResponseService::codeError(GatewayCode::SYSTEM_ERROR);
            }
            DB::beginTransaction();
            $orderInfo = $this->orderService->storeOrder($request, $platuser, $group_payment, $recharge_if);
            $tokenField = csrf_field();
            $token = md5($orderInfo['merchantNo'] . time());
            $request->session()->flash('post_token', $token);
            $url = '';
            if (PaymentMap::isBankHref($data['recharge_type'])) {
                $url = route('gateway.recharge.checkout.bank');
            }
            if (PaymentMap::isScanCode($data['recharge_type'])) {
                $url = route('gateway.recharge.checkout.qrcode');
            }
            if (PaymentMap::isWapReq($data['recharge_type'])) {
                $agent = new Agent();
                if ( ! $agent->isMobile()) {
                    return GatewayResponseService::codeError(GatewayCode::PLATFORM_NOTMATCH);
                }
                $url = route('gateway.recharge.checkout.wap');
            }
            if (PaymentMap::isSdkReq($data['recharge_type'])) {
                $url = route('gateway.recharge.checkout.sdk');
            }
            $form = <<<form
<body onload="autoPost();">
<form action="{$url}" id="checkout" method="post" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="merchantNo" value="{$orderInfo['merchant_no']}">
{$tokenField}
<input type="hidden" name="postToken" value="{$token}">
</form>
</body><script type="text/javascript">function autoPost() {
  document.getElementById('checkout').submit()
}</script>
form;
            DB::commit();
            return response((new HtmlString($form)));
        } catch (ValidatorException $exception) {
            return GatewayResponseService::fieldError($exception->getMessageBag()->getMessages());
        } catch (\Exception $exception) {
            DB::rollback();
            RechargeLog::common('INFO', $exception);
            return GatewayResponseService::codeError(GatewayCode::SYSTEM_ERROR, $exception->getMessage());
        }
    }


    public function callback(Request $request,
                             RechargeOrderNotifyService $rechargeOrderNotifyService,
                             $identify)
    {
        try{
            DB::beginTransaction();
            $third_if = $this->rechargeFactory->getInstance($identify);
            $notify_data = $request->all();
            if (empty($notify_data)) {
                $notify_data = json_decode(file_get_contents("php://input"), true);
            }
            if ($third_if->isReturnInCallback($notify_data)) {
                $href_url = $third_if->getReturnUrl() . '?' . http_build_query($notify_data);
                return redirect()->to($href_url);
            }
            echo $third_if->showSuccess();
            $notify_data = filterArrayEncode($notify_data);
            Log::info(json_encode($notify_data));
            $orderNo = $third_if->getOrderNoFromCallback($notify_data);
            $orderAmt = $third_if->getOrderAmtFromCallback($notify_data);
            if ($orderNo) {
                $order = RechargeOrder::where([
                    'plat_no' => $orderNo,
//                    'order_amt' => $orderAmt
                ])->where('order_status', 0)->first();
                if ($order) {
                    $order = $this->orderService->updateOrderThirdNotify($order, $notify_data, $third_if->getThirdNoFromCallback($notify_data));
                    $third_if->initMchProfile($order->upperIf);
                    if ($third_if->callback($notify_data)) {
                        $order = $this->orderService->updateOrder($order);
                        $this->orderService->settleOrder($order);
                        DB::commit();
                        $notify = $rechargeOrderNotifyService->createNewNotify($order);
                        SendRechargeCallback::dispatch($notify)->onQueue('recharge.callback');
                    } else {
                        Log::info('['.$third_if->getIdentify()."]验证签名失败 ----> " .$orderNo);

                        echo 'sign failed';
                    }
                }
            } else {
                Log::info('['.$third_if->getIdentify()."]无订单字段");
                echo 'notify data has no order field';
            }
        } catch (\Exception $exception) {
            DB::rollback();
            Log::info('['.$third_if->getIdentify()."]异步回调失败! \r\n 回调json[".json_encode($notify_data)."] \t [error]:".$exception->getMessage());
            echo 'system error';
            echo $exception->getMessage();
        }
    }


    public function returnHref(Request $request,
                               RechargeOrderNotifyService $rechargeOrderNotifyService,
                               $identify,
                               $orderNo = null)
    {
//        $str = '{"sign": "d613c51d5b15eb19bab157bf25365d0c", "remark": "eyJ1SWQiOjEsIm9yZGVySWQiOjExfQ==", "result": "1", "pay_amt": "1.00", "agent_id": "1664502", "pay_type": "30", "pay_message": null, "jnet_bill_no": "H1712232371060AP", "agent_bill_id": "RN201712232319103509"}';
        $third_if = $this->rechargeFactory->getInstance($identify);
        $notify_data = $request->all();
//        $notify_data = json_decode($str, true);
        if (empty($notify_data)) {
            $notify_data = json_decode(file_get_contents("php://input"), true);
        }
        $orderNo = $orderNo === null ? $third_if->getOrderNoFromCallback($notify_data) : $orderNo;
        if ($orderNo) {
            $order = RechargeOrder::where([
                'plat_no' => $orderNo
            ])->first();
            if (! $order) {
                return response('invalidate order !');
            }
            $mchData = $order->order_data;
            $mchReturnUrl = $mchData['return_url'];
            $params = $rechargeOrderNotifyService->getNotifyParams($order);
            return redirect($mchReturnUrl . '?' . http_build_query($params));
        }
        return response('invalidate request');

    }

    public function orderIsValidate(Request $request)
    {
        $orderNo = $request->input('orderNo');
        $order = RechargeOrder::where('plat_no', $orderNo)->first(['order_status']);
        if (! $order) {
            return response()->json(['code' => -1]);
        }
        if ($order->order_status == 1) {
            return response()->json(['code' => 1]);
        }
        if (Carbon::parse($order->created_at)->addMinutes(5)->toDateTimeString() < Carbon::now()->toDateTimeString()) {
            return response()->json(['code' => 2]);
        }
        return response()->json(['code' => 0]);
    }


    public function paySuccess(Request $request,
                               RechargeOrderNotifyService $rechargeOrderNotifyService)
    {
        $orderNo = $request->input('orderNo');
        $order = RechargeOrder::where([
            'plat_no' => $orderNo
        ])->where('order_status', 1)->first();
        if ($order) {
            $mchData = $order->order_data;
            $mchReturnUrl = $mchData['return_url'];
            $params = $rechargeOrderNotifyService->getNotifyParams($order);
            return redirect($mchReturnUrl . '?' . http_build_query($params));
        } else {
            return redirect()->route('gateway.recharge.expired');
        }

    }

    public function pageError(Request $request)
    {
        return view('gateway.recharge.error');
    }

    public function queryOrder(Request $request)
    {
        $data = $request->all();
        try {
            $this->validator->with($data)->passesOrFail('query');
        } catch (ValidatorException $exception) {
            return GatewayResponseService::fieldError($exception->getMessageBag()->getMessages());
        }
        $user = PlatUser::bycode($data['mch_code'])->select('id', 'key')->first();
        $sign = $data['sign'];
        array_forget($data, 'sign');
        ksort($data);
        $signStr = '';
        foreach ($data as $k => $v) {
            $signStr .= "{$k}={$v}&";
        }
        $signStr .= $user->key;
        if (md5($signStr) == $sign) {
            $order = RechargeOrder::bymchno($data['mch_no'])
                ->where('uid', $user->id)
                ->select('id', 'plat_no', 'app_id', 'order_status', 'order_amt', 'created_at', 'updated_at')
                ->first();
            if ( ! $order) {
                return GatewayResponseService::codeError(GatewayCode::ORDER_MCH_NOT_MATCH);
            }
            $resInfo['state'] = $order->order_status;
            $resInfo['system_no'] = $order->plat_no;
            $resInfo['mch_no'] = $data['mch_no'];
            $resInfo['mch_code'] = $data['mch_code'];
            $resInfo['order_amt'] = $order->order_amt;
            $resInfo['created_at'] = $order->created_at;
            $resInfo['done_at'] = $order->order_status == 1 ? $order->updated_at : '-';
            $resInfo['appInfo'] = [
                'name' => $order->app->name,
                'id' => $order->app->app_id
            ];
            return GatewayResponseService::success($resInfo);
        } else {
            return GatewayResponseService::fieldError(['sign' => GatewayCode::SIGN_NOT_MATCH]);
        }
    }
}
